package br.ufpe.cin.imlm2.util.options;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import br.ufpe.cin.imlm2.util.SystemProperties;

public class OptionsParser extends Options {

	public static final String PARAM_PREFIX = "--";
	public static final String ARGUMENT_SEPARATOR = ":";
	private String programName;
	private String usage;

	public OptionsParser( String programName ) {
		super();
		this.programName = programName;
		this.usage = this.initUsage();
		this.addOption( new Option( "help", "h", "Shows this help.", Boolean.FALSE ) );
	}

	private String initUsage() {
		String usage = "Usage:\n ";
		String progName = this.programName != null ? this.programName : "PROGRAM";
		usage += progName + " options";
		return usage;
	}

	public boolean isShowHelp() {
		return (Boolean) this.getOptionInternal( "help" ).getValue();
	}

	public String getUsage() {
		return this.usage;
	}

	public void setUsage( String usage ) {
		this.usage = usage;
	}

	public String getHelp() {
		String help = this.usage == null ? "" : this.usage;
		help = help + "\nValid options are:\n" + this.getParametersHelp();
		return help;
	}

	protected String getParametersHelp() {
		StringBuffer phaseOptions = new StringBuffer();
		for ( Option op : this.getOptions() ) {
			phaseOptions.append( '\t' ).append( "--" ).append( op.getName() );
			if ( !op.getName().equals( op.getAlias() ) )
				phaseOptions.append( '(' ).append( op.getAlias() ).append( ')' );
			Object defaultVal = op.getDefaultValue();
			String defVal = defaultVal == null ? "" : defaultVal.toString();
			if ( op.getType().equals( List.class ) ) {
				@SuppressWarnings("unchecked")
				List<Object> list = (List<Object>) op.getDefaultValue();
				defVal = "";
				for ( Object object : list ) {
					defVal += object.toString() + ",";
				}
				if ( !defVal.isEmpty() )
					defVal = defVal.substring( 0, defVal.length() - 1 );
			}
			phaseOptions.append( ':' ).append( "'" ).append( defVal ).append( "'" );
			if ( op.getDescription() != null )
				phaseOptions.append( " - " )
						.append( op.getDescription() )
						.append( SystemProperties.LINE_SEPARATOR );

		}
		if ( phaseOptions.length() > 0 )
			phaseOptions.deleteCharAt( phaseOptions.length() - 1 );
		return phaseOptions.toString();
	}

	public List<String> parse( String[] args ) throws ParsingException {
		List<String> argsList = Arrays.asList( args );
		List<String> unparsedList = new ArrayList<String>();
		Map<String, String> options = new HashMap<String, String>();
		for ( String str : argsList ) {

			if ( str.startsWith( PARAM_PREFIX ) ) {
				str = str.replaceFirst( PARAM_PREFIX, "" );
				if ( str.contains( ARGUMENT_SEPARATOR ) ) {
					String[] parts = str.split( ARGUMENT_SEPARATOR, 2 );
					if ( parts.length != 2 )
						throw new ParsingException( "Unable to parse token: " + str );
					options.put( parts[0], parts[1] );
				} else {
					options.put( str, Boolean.TRUE.toString() );
				}
			} else
				unparsedList.add( str );

		}

		try {
			this.parse( options );
		} catch ( NoSuchOptionException e ) {
			throw new ParsingException( "Unkown argument " + e.getInvalidOtion() );
		}
		if ( this.isShowHelp() )
			System.out.println( this.getHelp() );
		return unparsedList;
	}

}
